﻿#region Header
//
//   Project:           SilverShader - Silverlight pixel shader demo application for Coding4Fun.
//
//   Changed by:        $Author$
//   Changed on:        $Date$
//   Changed in:        $Revision$
//   Project:           $URL$
//   Id:                $Id$
//
//
//   Copyright (c) 2010 Rene Schulte
//
//   This program is open source software. Please read the License.txt.
//
#endregion

using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Imaging;


namespace Schulte.Silverlight.Shader
{
   public class ScratchedShaderEffect : ShaderEffect
   {
      #region Fields

      private Random rand = new Random();

      #endregion

      #region DependencyProperties

      public static DependencyProperty InputProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("Input", typeof(ScratchedShaderEffect), 0);
      public static DependencyProperty NoiseProperty = ShaderEffect.RegisterPixelShaderSamplerProperty("NoiseSampler", typeof(ScratchedShaderEffect), 1);
      public static DependencyProperty ScratchAmountProperty = DependencyProperty.Register("ScratchAmount", typeof(float), typeof(ScratchedShaderEffect), new PropertyMetadata(0.0044f, ShaderEffect.PixelShaderConstantCallback(0)));
      public static DependencyProperty NoiseAmountProperty = DependencyProperty.Register("NoiseAmount", typeof(float), typeof(ScratchedShaderEffect), new PropertyMetadata(0.000001f, ShaderEffect.PixelShaderConstantCallback(1)));
      public static DependencyProperty RandomCoord1Property = DependencyProperty.Register("RandomCoord1", typeof(Point), typeof(ScratchedShaderEffect), new PropertyMetadata(new Point(0, 0), ShaderEffect.PixelShaderConstantCallback(2)));
      public static DependencyProperty RandomCoord2Property = DependencyProperty.Register("RandomCoord2", typeof(Point), typeof(ScratchedShaderEffect), new PropertyMetadata(new Point(0, 0), ShaderEffect.PixelShaderConstantCallback(3)));
      public static DependencyProperty FrameProperty = DependencyProperty.Register("Frame", typeof(float), typeof(ScratchedShaderEffect), new PropertyMetadata(0f, ShaderEffect.PixelShaderConstantCallback(4)));
      
      #endregion

      #region Properties

      public virtual System.Windows.Media.Brush Input
      {
         get
         {
            return ((System.Windows.Media.Brush)(GetValue(InputProperty)));
         }
         set
         {
            SetValue(InputProperty, value);
         }
      }

      public virtual System.Windows.Media.Brush NoiseImage
      {
         get
         {
            return ((System.Windows.Media.Brush)(GetValue(NoiseProperty)));
         }
         set
         {
            SetValue(NoiseProperty, value);
         }
      }

      public virtual float ScratchAmount
      {
         get
         {
            return ((float)(GetValue(ScratchAmountProperty)));
         }
         set
         {
            SetValue(ScratchAmountProperty, value);
         }
      }

      public virtual float NoiseAmount
      {
         get
         {
            return ((float)(GetValue(NoiseAmountProperty)));
         }
         set
         {
            SetValue(NoiseAmountProperty, value);
         }
      }

      public virtual Point RandomCoord1
      {
         get
         {
            return ((Point)(GetValue(RandomCoord1Property)));
         }
         set
         {
            SetValue(RandomCoord1Property, value);
         }
      }  

      public virtual Point RandomCoord2
      {
         get
         {
            return ((Point)(GetValue(RandomCoord2Property)));
         }
         set
         {
            SetValue(RandomCoord2Property, value);
         }
      }

      public virtual float Frame
      {
         get
         {
            return ((float)(GetValue(FrameProperty)));
         }
         set
         {
            SetValue(FrameProperty, value);
         }
      }

      #endregion

      #region Contructors

      public ScratchedShaderEffect()
      {
         this.PixelShader = new PixelShader() { UriSource = new Uri("/SilverShader;component/Shaders/ScratchedShader.ps", UriKind.Relative) };
         this.UpdateShaderValue(InputProperty);
         this.UpdateShaderValue(NoiseProperty);
         this.UpdateShaderValue(ScratchAmountProperty);
         this.UpdateShaderValue(NoiseAmountProperty);
         this.UpdateShaderValue(RandomCoord1Property);
         this.UpdateShaderValue(RandomCoord2Property);
         this.UpdateShaderValue(FrameProperty);

         // Generate noise texture and attach to shader
         this.NoiseImage = GenerateNoiseTexture();

         CompositionTarget.Rendering += new EventHandler(CompositionTarget_Rendering);
      }

      #endregion   

      #region Methods

      private Brush GenerateNoiseTexture()
      {
         // Generate random image for pixel shader lookup
         const int NoiseTexSize = 1024;
         int size = NoiseTexSize * NoiseTexSize;
         var writeableBmp = new WriteableBitmap(NoiseTexSize, NoiseTexSize);
         for (int i = 0; i < size; i++)
         {
            writeableBmp.Pixels[i] = rand.Next();
         }

         // Create ImageBrush
         writeableBmp.Invalidate();
         ImageBrush noiseImg = new ImageBrush();
         noiseImg.ImageSource = writeableBmp;
         return noiseImg;
      }

      #endregion

      #region Eventhandler

      private void CompositionTarget_Rendering(object sender, EventArgs e)
      {
         // Random seeds for shader noise texture lookup and frame increment
         this.RandomCoord1 = new Point(rand.NextDouble(), rand.NextDouble());
         this.RandomCoord2 = new Point(rand.NextDouble(), rand.NextDouble());
         this.Frame++;
      }

      #endregion
   }
}
